/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package systole.math;

import java.math.BigDecimal;
import systole.domain.signals.FinalSignal;
import systole.domain.signals.Segment;

/**
 *
 * @author user
 */
public class DividedDifferences implements Derivative {

    public FinalSignal derivate(Segment f) {

        FinalSignal derivatives = new FinalSignal();

        derivatives.setFinalSegment(f);
        derivatives.setFirstDerivatite(this.getFirstDerivative(f));
        derivatives.setSecondDerivative(this.getSecondDerivative(f));
        derivatives.setThirdDerivative(this.getThirdDerivative(f));
        derivatives.setFourthDerivative(this.getFourthDerivative(f));
        return derivatives;

    }

    public Segment getFirstDerivative(Segment segment) {
        int nPoints = segment.size();
        int h = 1;  //h interval taken
        int step = 1;
        BigDecimal diff = BigDecimal.ZERO;

        Segment derivated = new Segment();

        //f'(x) = f(x+h) - f(x-h)/h ; En este caso, h = 1
        for (int j = 0; j < nPoints - 2; j++) {

            diff = (segment.elementAt(j + h).subtract(segment.elementAt(j))).divide(new BigDecimal(2 * h),MathUtils.CONTEXT);
            derivated.add(diff);
        }

        diff = derivated.elementAt(1).subtract(derivated.firstElement());
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        return derivated;

    }

    public Segment getSecondDerivative(Segment segment) {
        int nPoints = segment.size();
        BigDecimal h = BigDecimal.ONE;  //h interval taken
        BigDecimal diff = BigDecimal.ZERO;
        int step = 2;
        BigDecimal two = new BigDecimal(2);
        Segment derivated = new Segment();


        for (int j = 0; j < (nPoints - 3); j++) {

            //f''(x) = (f(x+2) - 2f(x+1) + f(x))/h^2 ; En este caso, h = 1
            diff = (segment.elementAt(j + 2).subtract(two.multiply(segment.elementAt(j + 1))).add(segment.elementAt(j).divide(h.pow(step),MathUtils.CONTEXT)));

            derivated.add(diff);
        }
        diff = derivated.elementAt(1).subtract(derivated.firstElement());
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        return derivated;

    }

    public Segment getThirdDerivative(Segment segment) {
        int nPoints = segment.size();
        BigDecimal h = BigDecimal.ONE;  //h interval taken
        int step = 3;
        BigDecimal three = new BigDecimal(3);
        BigDecimal diff = BigDecimal.ZERO;

        Segment derivated = new Segment();

        //f'''(x) = (f(x+3) - 3f(x+2) + 3f(x+1) -f(x))/h^3 ; En este caso, h = 1
        for (int j = 0; j < (nPoints - 3); j++) {

            diff = ((segment.elementAt(j + 3)).subtract(three.multiply(segment.elementAt(j + 2))).
                    add(three.multiply(segment.elementAt(j + 1))).
                    subtract(segment.elementAt(j))).divide(h.pow(step),MathUtils.CONTEXT);

            derivated.add(diff);
        }

        diff = derivated.elementAt(1).subtract(derivated.firstElement());
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        return derivated;
    }

    public Segment getFourthDerivative(Segment segment) {
        int nPoints = segment.size();
        BigDecimal h = BigDecimal.ONE;  //h interval taken
        int step = 4;
        BigDecimal four = new BigDecimal(4);
        BigDecimal six = new BigDecimal(6);
        BigDecimal diff = BigDecimal.ZERO;

        Segment derivated = new Segment();

        //f'''(x) = (f(x+4) - 4f(x+3) + 6f(x+2) -4f(x+1) + f(x))/h^3 ; En este caso, h = 1
        for (int j = 0; j < (nPoints - 4); j++) {

            diff = ((segment.elementAt(j + 4)).subtract(
                    (four.multiply(segment.elementAt(j + 3)))).add(
                    (six.multiply(segment.elementAt(j + 2)))).subtract(
                    (four.multiply(segment.elementAt(j + 1)))).add(
                    (segment.elementAt(j)))).divide(h.pow(step),MathUtils.CONTEXT);

            derivated.add(diff);
        }

         diff = derivated.elementAt(1).subtract(derivated.firstElement());
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        derivated.insertElementAt(derivated.firstElement().subtract(diff), 0);
        return derivated;
    }
}
